﻿using System.Collections.Generic;
using System.Diagnostics;
using Internal.UIOC;
using UIOC;
using UIOC.Attributes;

namespace UIOC {

    public interface IBean : IObject {

        InstanceType InstanceType { get; }
    }

#if !DISABLE_DEBUGGER_ATTRIBUTE
    [DebuggerStepThrough]
    [DebuggerNonUserCode]
#endif
    public abstract class SingletonBean : Bean, IPooledObjectInternal, IObjectInternal, ISingletonBeanInternal {

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        public sealed override InstanceType InstanceType { get; } = InstanceType.Singleton;

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IPooledObjectInternal.Allocate() {
            AssertDispose();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void ISingletonBeanInternal.Allocate() {
            (Object as IPooledObjectInternal).Allocate();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IPooledObjectInternal.Release() {
            AssertDispose();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IObjectInternal.Construct() {
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IObjectInternal.Dispose() {
            AssertDispose();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void ISingletonBeanInternal.Dispose() {
            Dispose();
        }
    }

#if !DISABLE_DEBUGGER_ATTRIBUTE
    [DebuggerStepThrough]
    [DebuggerNonUserCode]
#endif
    public abstract class DefaultBean : Bean, IPooledObjectInternal, IObjectInternal {

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        public sealed override InstanceType InstanceType { get; } = InstanceType.Default;

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IPooledObjectInternal.Allocate() {
            AssertDispose();
            (Object as IPooledObjectInternal).Allocate();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IPooledObjectInternal.Release() {
            AssertDispose();
            (Object as IPooledObjectInternal).Release();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IObjectInternal.Construct() {
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IObjectInternal.Dispose() {
            AssertDispose();
            Dispose();
        }
    }

#if !DISABLE_DEBUGGER_ATTRIBUTE
    [DebuggerStepThrough]
    [DebuggerNonUserCode]
#endif
    public abstract class Bean : IBeanInternal, IObject {

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        public abstract InstanceType InstanceType { get; }

        private IBeanObjectInternal m_Object;
#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        protected IBeanObjectInternal Object => m_Object;
#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        IBeanObjectInternal IBeanInternal.Object {
            get {
                AssertDispose();
                return m_Object;
            }
        }

        private bool m_IsDisposed;
#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        protected bool IsDisposed => m_IsDisposed;
#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        bool IObject.IsDisposed {
            get {
                AssertDispose();
                return m_IsDisposed;
            }
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        protected void Dispose() {
            m_Object = null;
            m_IsDisposed = true;
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        void IBeanInternal.Initialize(IBeanObjectInternal @object) {
            AssertDispose();
            m_Object = @object;
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        bool IBeanInternal.BeanEquals(IBeanInternal obj) {
            return base.Equals(obj);
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        int IBeanInternal.GetBeanHashCode() {
            return base.GetHashCode();
        }

#if !DISABLE_DEBUGGER_ATTRIBUTE
        [DebuggerHidden]
#endif
        public sealed override string ToString() {
            AssertDispose();
            return m_Object.ToString();
        }

        [Conditional("UNITY_EDITOR")]
        [Conditional("DEBUG")]
        [DebuggerHidden]
        protected void AssertDispose() {
            if (m_IsDisposed) {
                throw new System.ObjectDisposedException("正在使用已经销毁的对象");
            }
        }
    }
}

namespace Internal.UIOC {

    public interface IBeanInternal : IBean {

        IBeanObjectInternal Object { get; }

        void Initialize(IBeanObjectInternal @object);

        bool BeanEquals(IBeanInternal bean);

        int GetBeanHashCode();
    }

    public interface ISingletonBeanInternal : IBeanInternal {

        void Allocate();

        void Dispose();
    }

    public interface IAutowiredInternal {

        void Construct();

        void Allocate();

        void Release();

        void Dispose();
    }

    [DebuggerStepThrough]
    [DebuggerNonUserCode]
    internal sealed class BeanInternalEqualityComparer : EqualityComparer<IBeanInternal> {

        public new static BeanInternalEqualityComparer Default = new();

        [DebuggerHidden]
        public override bool Equals(IBeanInternal x, IBeanInternal y) {
            return x.BeanEquals(y);
        }

        [DebuggerHidden]
        public override int GetHashCode(IBeanInternal obj) {
            return obj.GetBeanHashCode();
        }
    }
}